package com.afdor.rws.core.extension.factory;

import com.afdor.rws.annotation.extension.Extension;
import com.afdor.rws.core.extension.ExtensionFactory;
import com.afdor.rws.core.extension.ExtensionItem;
import com.afdor.rws.core.extension.utils.ExtensionUtils;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;

import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

/**
 * 基于Reflections实现的扩展点扫描加载工厂实现
 *
 * @author 悭梵
 * @date Created in 2018-09-13 14:47
 */
@Slf4j
@ToString
@EqualsAndHashCode
@Extension(value = "scaner", order = Extension.MAX_ORDER - 100, interfaceClass = ExtensionFactory.class)
public class ScanerExtensionFactory extends DefaultExtensionFactory {


    public ScanerExtensionFactory() {
        super(new ScanerExtensionClassesLoader());
    }

    @Override
    public <T> boolean hasExtension(Class<T> interfaceClass, String name) {
        String fullExtensionName = ExtensionUtils.getFullExtension(interfaceClass, name);
        ConcurrentMap<String, Class<?>> interfaceExtensionClassMap = CACHED_INTERFACE_EXTENSION_CLASSES.get(interfaceClass);
        return interfaceExtensionClassMap != null && interfaceExtensionClassMap.containsKey(fullExtensionName);
    }

    @Override
    public <T> T newExtension(Class<T> interfaceClass, String name, Object... args) {
        T extension = null;
        String fullExtensionName = ExtensionUtils.getFullExtension(interfaceClass, name);
        ConcurrentMap<String, Class<?>> interfaceExtensionClassMap = CACHED_INTERFACE_EXTENSION_CLASSES.get(interfaceClass);
        if (interfaceExtensionClassMap != null && interfaceExtensionClassMap.containsKey(fullExtensionName)) {
            extension = createNewExtension(interfaceClass, fullExtensionName, (Class<T>) interfaceExtensionClassMap.get(fullExtensionName), args);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

    @Override
    public <T> T getExtension(Class<T> interfaceClass, String name) {
        T extension = null;
        String fullExtensionName = ExtensionUtils.getFullExtension(interfaceClass, name);
        ConcurrentMap<String, Class<?>> interfaceExtensionClassMap = CACHED_INTERFACE_EXTENSION_CLASSES.get(interfaceClass);
        if (interfaceExtensionClassMap != null && interfaceExtensionClassMap.containsKey(fullExtensionName)) {
            extension = createExtension(interfaceClass, fullExtensionName);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

    @Override
    public <T> Collection<ExtensionItem<T>> loadExtensionClasses(Class<T> interfaceClass) {
        // 自身业务
        ConcurrentMap<String, Class<?>> interfaceClassMap = CACHED_INTERFACE_EXTENSION_CLASSES.get(interfaceClass);
        if (interfaceClassMap == null) {
            CACHED_INTERFACE_EXTENSION_CLASSES.putIfAbsent(interfaceClass, new ConcurrentHashMap<String, Class<?>>(32));
            interfaceClassMap = CACHED_INTERFACE_EXTENSION_CLASSES.get(interfaceClass);
        }
        ConcurrentMap<String, Class<?>> finalInterfaceClassMap = interfaceClassMap;
        Collection<ExtensionItem<T>> extensionItemList = Optional.ofNullable(getExtensionClassesLoader().loadExtensionClasses(interfaceClass)).orElse(Collections.emptySet());
        extensionItemList.forEach(item -> finalInterfaceClassMap.putIfAbsent(item.getName(), item.getExtensionClass()));
        return extensionItemList;
    }

    /**
     * 基于文件配置的ExtensionClassesLoader
     */
    static class ScanerExtensionClassesLoader implements ExtensionClassesLoader {

        @Override
        public <T> Collection<ExtensionItem<T>> loadExtensionClasses(final Class<T> interfaceClass) {

            Reflections reflections = new Reflections(new ConfigurationBuilder()
                    .setUrls(ClasspathHelper.forPackage(getStartPackage()))
                    .filterInputsBy((path) -> {
                        if (!StringUtils.contains(path, "/") && StringUtils.endsWith(path, ".class")) {
                            try {
                                Class<?> clazz = Class.forName(StringUtils.substring(path, 0, path.length() - 6));
                                return !clazz.isInterface() && !clazz.isEnum() && interfaceClass.isAssignableFrom(clazz);
                            } catch (Exception e) {
                            }
                        }
                        return false;
                    }));

            return (Collection) Optional.ofNullable(reflections.getTypesAnnotatedWith(Extension.class))
                    .orElse(Collections.emptySet())
                    .stream()
                    .map(clazz -> {
                        if (clazz.getAnnotation(Extension.class) == null) {
                            return null;
                        }
                        return ExtensionItem.builder()
                                .name(ExtensionUtils.getFullExtension(interfaceClass, clazz.getAnnotation(Extension.class).value()))
                                .extensionClass((Class) clazz)
                                .build();
                    })
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());
        }

        /**
         * 指定明确的扫描起始点，有助于扫描性能提升。仅确定起始点，不支持正则，必须是明确的包路径，默认所有路径
         *
         * @return
         */
        private String getStartPackage() {
            String startPackage = System.getProperty("extension.scaner.start.package");
            if (StringUtils.isBlank(startPackage)) {
                startPackage = System.getenv("extension.scaner.start.package");
            }
            if (StringUtils.isBlank(startPackage)) {
                startPackage = ".";
            }
            return startPackage;
        }
    }
}
